﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.Concurrent;
using Settings;

namespace StorageManagement
{
    public class DictionaryStorage<TKey, T> where T : class
    {
        private static ConcurrentDictionary<TKey, ConcurrentQueue<T>> _dictData = new ConcurrentDictionary<TKey, ConcurrentQueue<T>>();
        
        /// <summary>
        /// Tập các key của Storage
        /// </summary>
        protected static ICollection<TKey> GetKeys()
        {
            return _dictData.Keys;
        }
        
        /// <summary>
        /// Đẩy dữ liệu vào Storage theo key dữ liệu
        /// </summary>
        public static void EnqueueElementData(TKey keyData, T data)
        {
            if (!_dictData.ContainsKey(keyData))
                _dictData.TryAdd(keyData, new ConcurrentQueue<T>());
            if (_dictData.ContainsKey(keyData))
            {
                _dictData[keyData].Enqueue(data);
            }
        }

        /// <summary>
        /// Đẩy 1 tập dữ liệu vào Storage theo key dữ liệu
        /// </summary>
        public static void EnqueueElementData(TKey keyData, IEnumerable<T> data)
        {
            if (!_dictData.ContainsKey(keyData))
                _dictData.TryAdd(keyData, new ConcurrentQueue<T>());
            if (_dictData.ContainsKey(keyData))
            {
                foreach (var item in data)
                    _dictData[keyData].Enqueue(item);
            }
        }

        /// <summary>
        /// Lấy dữ liệu từ Storage và xóa dữ liệu đã được lấy ra khỏi Storage
        /// </summary>
        public static T DequeueElementData(TKey keyData)
        {
            if (!_dictData.ContainsKey(keyData))
                return null;
            T retValue = null;
            _dictData[keyData].TryDequeue(out retValue);
            return retValue;
        }

        /// <summary>
        /// Lấy 1 tập dữ liệu từ Storage và xóa dữ liệu đã được lấy ra khỏi Storage
        /// </summary>
        /// <returns></returns>
        public static List<T> DequeueElementData(TKey keyData, int countElement)
        {
            List<T> retList = new List<T>();
            if (!_dictData.ContainsKey(keyData))
                return retList;
            ConcurrentQueue<T> queue = _dictData[keyData];
            while (retList.Count < countElement && !queue.IsEmpty)
            {
                T dequeuElement = null;
                queue.TryDequeue(out dequeuElement);
                if (dequeuElement != null)
                    retList.Add(dequeuElement);
            }
            return retList;
        }

        /// <summary>
        /// Lấy toàn bô dữ liệu từ Storage theo Key và xóa dữ liệu đã được lấy ra khỏi Storage
        /// </summary>
        /// <returns></returns>
        public static List<T> DequeueAllElementData(TKey keyData)
        {
            List<T> retList = new List<T>();
            if (!_dictData.ContainsKey(keyData))
                return retList;
            ConcurrentQueue<T> queue = _dictData[keyData];
            while (!queue.IsEmpty)
            {
                T dequeuElement = null;
                queue.TryDequeue(out dequeuElement);
                if (dequeuElement != null)
                    retList.Add(dequeuElement);
            }
            return retList;
        }

        /// <summary>
        /// Lấy toàn bộ dữ liệu từ Stored
        /// </summary>
        public static List<T> DequeueAllElementData()
        {
            List<T> retList = new List<T>();
            foreach (var key in _dictData.Keys)
            {
                retList.AddRange(DequeueAllElementData(key));
            }
            return retList;
        }

        /// <summary>
        /// Kiểm tra dữ liệu trong queue đã đầy hay chưa
        /// </summary>
        public static bool IsFull(TKey keyData)
        {
            if (!_dictData.ContainsKey(keyData))
                return false;
            return _dictData[keyData].Count > AppSetting.MaxQueueElement;
        }

        /// <summary>
        /// Lấy ra những key mà dữ liệu chưa full
        /// </summary>
        public static List<TKey> GetKeyNotFullData()
        {
            List<TKey> retValue = new List<TKey>();
            foreach (var item in _dictData)
            {
                if (!IsFull(item.Key))
                    retValue.Add(item.Key);
            }
            return retValue;
        }

        /// <summary>
        /// Lấy ra những key mà dữ liệu chưa full theo key đầu vào
        /// </summary>
        public static List<TKey> GetKeyNotFullData(IEnumerable<TKey> keysInput)
        {
            List<TKey> retValue = new List<TKey>();
            if (keysInput == null)
                return retValue;
            foreach (TKey item in keysInput)
            {
                if (!IsFull(item))
                    retValue.Add(item);
            }
            return retValue;
        }

        /// <summary>
        /// Tìm kiếm Items theo điều kiện 
        /// </summary>
        public static IEnumerable<T> FindItems(TKey key, Func<T, bool> predicate)
        {
            if (!_dictData.ContainsKey(key))
                Enumerable.Empty<T>();
            return _dictData[key].Where(predicate);
        }

        /// <summary>
        /// Tìm kiếm Items theo điều kiện 
        /// </summary>
        public static IList<T> FindItems(Func<T, bool> predicate)
        {
            var retValue = new List<T>();
            foreach (TKey key in _dictData.Keys)
            {
                retValue.AddRange(FindItems(key, predicate));
            }
            return retValue;
        }

        /// <summary>
        /// Tìm kiếm Item theo điều kiện 
        /// </summary>
        public static T FindItem(TKey key, Func<T, bool> predicate)
        {
            if (!_dictData.ContainsKey(key))
                return null;
            return _dictData[key].FirstOrDefault(predicate);
        }

        /// <summary>
        /// Tìm kiếm Item theo điều kiện 
        /// </summary>
        public static T FindItem(Func<T, bool> predicate)
        {
            foreach (TKey key in _dictData.Keys)
            {
                T retValue = FindItem(key, predicate);
                if (retValue != null)
                    return retValue;
            }
            return null;
        }
    }
}
